using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Services;
using Sudoku.Utilities;
using System.Diagnostics;
using System.Xml.Serialization;
using System.IO;
using System.Runtime.Serialization;


namespace Sudoku.GamePlay
{
	
	public interface IBoard
	{
		BoardCell GetCell(int x, int y);
		int GetTime();
		BoardCell GetSelectedCell();
		void SetSelectedCell(BoardCell cell);
		void SetSelectedCellValue(byte value);
		void SetSelectedCellGuess(int value);
		bool IsValid();
		Score GetScore();
		void Reset();
		void GetHint();
		short GetHintLeft();

		// To save and load...
		BoardSaveState GetBoardSaveState();

		event Board.BoardEvent OnGridComplete;
	}

	public class BoardSaveState
	{
		public class BoardCellSaveState
		{
			public byte Value;
			public bool[] Guess;
			public bool Fixed;

			public BoardCellSaveState()
			{
				Value = 0;
				Guess = new bool[9];
				Fixed = false;
			}
		}

		public Score score;
		public BoardCellSaveState[] grid;
		public BoardCellSaveState[] playerGrid;
		public short leftCellsCount;
		public short startingHints;

		public BoardSaveState()
		{
			score = new Score();
			grid = new BoardCellSaveState[81];
			playerGrid = new BoardCellSaveState[81];
			leftCellsCount = 0;
			startingHints = 0;
		}
	}

	public class Board : GameComponent, IBoard //, ILevelManager<Score>
	{
		public delegate void BoardEvent(Boolean isGridValid);
		public event BoardEvent OnGridComplete;
		
		Score score;
		BoardCell[,] grid = new BoardCell[9, 9];
		BoardCell[,] playerGrid = new BoardCell[9, 9];

		BoardCell selected = null;
		short leftCellsCount;
		short startingHints;

		Random rnd;

		#region INIT

		public Board(Game game, BoardSaveState save)
			: base(game)
		{
			for (int i = 0; i < 9; ++i)
			{
				for (int j = 0; j < 9; ++j)
				{
					grid[i, j] = new BoardCell();
					grid[i, j].Value = save.grid[i * 9 + j].Value;
					for (int k = 0; k < 9; ++k) grid[i, j].Guess[k] = save.grid[i * 9 + j].Guess[k];
					grid[i, j].Fixed = save.grid[i * 9 + j].Fixed;

					playerGrid[i, j] = new BoardCell();
					playerGrid[i, j].Value = save.playerGrid[i * 9 + j].Value;
					for (int k = 0; k < 9; ++k) playerGrid[i, j].Guess[k] = save.playerGrid[i * 9 + j].Guess[k];
					playerGrid[i, j].Fixed = save.playerGrid[i * 9 + j].Fixed;
				}
			}

			leftCellsCount = save.leftCellsCount;
			startingHints = save.startingHints;
			score = save.score;

			CommonInit();
		}

		private void CommonInit()
		{
			Game.Services.AddService(typeof(IBoard), this);
			UpdateOrder = (int)ComponentOrders.LOGIC;
			rnd = new Random(DateTime.Now.Millisecond);

			Game.Activated += new EventHandler<EventArgs>(OnActivated);
			Game.Deactivated += new EventHandler<EventArgs>(OnDeactivated);
			Game.Exiting += new EventHandler<EventArgs>(OnClosed);
		}

		protected override void Dispose(bool disposing)
		{
			Game.Services.RemoveService(typeof(IBoard));
			base.Dispose(disposing);

			Game.Activated -= new EventHandler<EventArgs>(OnActivated);
			Game.Deactivated -= new EventHandler<EventArgs>(OnDeactivated);
			Game.Exiting -= new EventHandler<EventArgs>(OnClosed);
		}

		#endregion

		#region Interface

		public BoardCell GetCell(int x, int y)
		{
			return playerGrid[x,y];
		}

		public int GetTime()
		{
			return (int)score.Time;
		}

		public void SetSelectedCell(BoardCell cell)
		{
			selected = cell;
		}

		public void SetSelectedCellValue(byte value)
		{
			if (selected != null)
			{
				if (selected.Value != 0 && value == 0) leftCellsCount++;
				else if (selected.Value == 0 && value != 0) leftCellsCount--;

				selected.Value = value;

				if (leftCellsCount == 0)
					if (OnGridComplete != null) OnGridComplete(IsValid());
			}
		}

		public void SetSelectedCellGuess(int value)
		{
			if(selected != null)
				selected.Guess[value] = !selected.Guess[value];
		}

		public BoardCell GetSelectedCell()
		{
			return selected;
		}

		public bool IsValid()
		{
			for (int y = 0; y < 9; ++y)
			{
				int sum = 0;
				for (int x = 0; x < 9; ++x)
				{
					sum += playerGrid[x, y].Value;
				}
				if (sum != 45) return false;
			}

			for (int x = 0; x < 9; ++x)
			{
				int sum = 0;
				for (int y = 0; y < 9; ++y)
				{
					sum += playerGrid[x, y].Value;
				}
				if (sum != 45) return false;
			}

			return true;
		}

		public Score GetScore()
		{
			return score;
		}

		public void Reset()
		{
			for (int y = 0; y < 9; ++y)
			{
				for (int x = 0; x < 9; ++x)
				{
					if (!playerGrid[x, y].Fixed) playerGrid[x, y].Value = 0;
					for (int g = 0; g < 9; ++g) 
						playerGrid[x, y].Guess[g] = false;
				}
			}
			leftCellsCount = 81;
			leftCellsCount -= startingHints;
			score.Hints = 0;
			score.Time = 0;
		}

		public void GetHint()
		{
			if (score.Hints >= 3) return;			//button should be disabled
			if (leftCellsCount == 0) return;

			score.Hints = score.Hints + 1;

			bool found = false;
			while (!found)
			{
				int x = rnd.Next(0, 9);
				int y = rnd.Next(0, 9);

				if (playerGrid[x, y].Value == 0)
				{
					SetSelectedCell(playerGrid[x, y]);
					SetSelectedCellValue(grid[x, y].Value);
					found = true;
				}
			}

		}

		public short GetHintLeft()
		{
			return (short)(3-score.Hints) ;
		}

		#endregion

		#region ISaveMethods

		private void OnClosed(object sender, EventArgs args)
		{
			//if it's disabled, don't save anything.
			if (!this.Enabled) return;

			this.Enabled = false;
			ISaveService saveService = (ISaveService)Game.Services.GetService(typeof(ISaveService));
			saveService.SaveGame();
		}

		private void OnActivated(object sender, EventArgs args)
		{
			this.Enabled = true;
			ISaveService saveService = (ISaveService)Game.Services.GetService(typeof(ISaveService));
			if (saveService.IsSaveAvailable())
			{
				StateManagement.StateManager.ContinueGame(Game, saveService.LoadGame());
			}
		}
		
		private void OnDeactivated(object sender, EventArgs args)
		{
			//if it's disabled, don't save anything.
			if (!this.Enabled) return;
			
			this.Enabled = false;
			ISaveService saveService = (ISaveService)Game.Services.GetService(typeof(ISaveService));
			saveService.SaveGame();
		}

		#endregion

		public override void Update(GameTime gameTime)
		{
			if (Enabled)
				score.Time += (float)gameTime.ElapsedGameTime.TotalSeconds;

			base.Update(gameTime);
		}

		public BoardSaveState GetBoardSaveState()
		{
			BoardSaveState saveState = new BoardSaveState();

			for (int i = 0; i < 9; ++i)
			{
				for (int j = 0; j < 9; ++j)
				{
					saveState.grid[i * 9 + j] = new BoardSaveState.BoardCellSaveState();
					saveState.grid[i * 9 + j].Value = grid[i, j].Value;
					for (int k = 0; k < 9; ++k) saveState.grid[i * 9 + j].Guess[k] = grid[i, j].Guess[k];
					saveState.grid[i * 9 + j].Fixed = grid[i, j].Fixed;

					saveState.playerGrid[i * 9 + j] = new BoardSaveState.BoardCellSaveState();
					saveState.playerGrid[i * 9 + j].Value = playerGrid[i, j].Value;
					for (int k = 0; k < 9; ++k) saveState.playerGrid[i * 9 + j].Guess[k] = playerGrid[i, j].Guess[k];
					saveState.playerGrid[i * 9 + j].Fixed = playerGrid[i, j].Fixed;
				}
			}

			saveState.leftCellsCount = leftCellsCount;
			saveState.startingHints = startingHints;
			saveState.score = score;

			return saveState;
		}
	}

}
